package com.shop.service.local.impl;

import com.google.gson.Gson;
import com.shop.controller.intoparam.TransferParam;
import com.shop.dao.AccountDao;
import com.shop.dao.DeDuplicationDao;
import com.shop.entity.Account;
import com.shop.service.fegin.MessageService;
import com.shop.service.local.AccountService;
import com.shop.util.SnowFlake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service
public class AccountServiceImpl implements AccountService {

    private Logger logger = LoggerFactory.getLogger(getClass().getName());

    @Autowired
    AccountDao accountDao;
    @Autowired
    MessageService messageService;
    @Autowired
    DeDuplicationDao deDuplicationDao;
    @Autowired
    SnowFlake snowFlake;

    /**
     * 转账
     * @param transferParam
     * @return java.lang.Integer
     *
     * 存在的问题：
     *      高并发的情况下，前面的线程刚修改完余额，后面的线程又知道到了获取before这里，导致后面的线程修改会覆盖前面的修改，一段时间内值并没有减少。
     *      但是事务ID又会不断生成。所以会出现，上游只减少了一点点值，下游却增加了好多值。
     *      所以，使用了 synchronized 对方法进行锁，但是作用有限
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void transfer(TransferParam transferParam) {
        // 解析参数
        Account account = new Account();
        BeanUtils.copyProperties(transferParam, account);

        // 从数据库获取账户信息，主要是余额信息
        Account before = accountDao.selectById(account.getId());
        logger.info("账户：{}，余额：{}", before.getUsername(), before.getMoney());

        // 1. 调用MQ服务发送消息
        Account message = new Account();
        message.setId(transferParam.getTargetId());
        message.setMoney(transferParam.getMoney());
        message.setTxId(snowFlake.nextId());
        Long messageId = messageService.addWaitSendMessage("shop-account.transfer.queue", new Gson().toJson(message));

        // 2. 执行业务，从转出账户中扣除余额
        account.setMoney(before.getMoney().subtract(account.getMoney()));
        account.setUpdateTime(new Date());
        accountDao.updateById(account);

        // 3. 调用MQ服务确认发送消息
        messageService.confirmAndSendMessage(messageId);
    }

}